
import React from 'react'
import cssStyle from './index.module.css'
import PropTypes from 'prop-types'

let context = React.createContext()
let Provider = context.Provider

class Carousel extends React.PureComponent{
    constructor(props){
        super(props)
        this.state = {
            current: 0,
            isMouseEnter: false,
            direction: '',
            length: 0,
            step: 1
        }
    }
    render(){
        return(
            <Provider value={{
                current: this.state.current,
                isMouseEnter: this.state.isMouseEnter,
                duration: this.props.duration,
                direction: this.state.direction,
                length: this.state.length,
                step: this.state.step,
                autoplay: this.props.autoplay,
                currentChange: current => {
                    this.setState(state => {
                        if (current === state.length) {
                            state.current = 0
                        } else {
                            state.current = current
                        }
                        return current
                    }, () => {
                        this.props.currentChange(this.state.current)
                    })
                },
                toggle: isMouseEnter => {
                    this.setState({
                        isMouseEnter
                    })
                },
                setDirection: (direction, step) => {
                    this.setState({
                        direction,
                        step
                    })
                },
                setLength: length => {
                    this.setState({
                        length
                    })
                }
            }}>
                <div className={ cssStyle.carousel }>{ this.props.children }</div>
            </Provider>
        )
    }
}

Carousel.propTypes = {
    children: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
    duration: PropTypes.number,
    currentChange: PropTypes.func,
    autoplay: PropTypes.bool
}
class Item extends React.PureComponent{
    static contextType = context
    constructor(props){
        super(props)
        this.timer = null
        this.timer1 = null
        this.slideRef = React.createRef()
        this.translateX = 0
        this.isMove = false
        this.current = 0
        this.isMouseEnter = false
        this.length = 0
    }
    moveResolve = current => {
        window.cancelAnimationFrame(this.timer1)
        this.timer1 = null
        this.current = current
        this.context.currentChange(current)
        this.context.setDirection('', 1)
        this.isMove = false
        this.translateX = -current * 100
    }
    move = (current) => {
        this.isMove = true
        this.timer1 = window.requestAnimationFrame(() => {
            let _current = current
            if (this.context.direction === 'next') {
                this.translateX -= 4 * this.context.step
            } else if (this.context.direction === 'prev') {
                this.translateX += 4 * this.context.step
            }
            if ((this.context.direction === 'next' && this.translateX <= -_current * 100) || (this.context.direction === 'prev' && this.translateX >= -_current * 100)) {
                this.moveResolve(_current)
            }
            this.slideRef.current.style.transform = `translateX(${this.translateX}%)`
            if (this.isMove) {
                this.move(current)
            }
        })
    }
    go = () => {
        if (this.timer) {
            clearTimeout(this.timer)
            this.timer = null
        }
        this.timer = setTimeout(() => {
            this.context.setDirection('next', 1)
        }, this.context.duration)
    }
    setCurrent = length => {
        if (this.context.direction === 'prev') {
            if (this.current === 0) {
                this.translateX = -length * 100
                this.current = length - this.context.step
            } else {
                this.current -= this.context.step
            }
        }
        if (this.context.direction === 'next') {
            if (this.current === length) {
                this.translateX = 0
                this.current = this.context.step
            } else {
                this.current += this.context.step
            }
        }
        this.slideRef.current.style.transform = `translateX(${this.translateX}%)`
        this.move(this.current)
    }
    componentDidUpdate(){
        this.isMouseEnter = this.context.isMouseEnter
        let length = this.props.render().length
        if (this.length !== length) {
            this.context.setLength(length)
        }
        this.length = length
        if (!this.isMove && this.context.direction) {
            this.setCurrent(length)
            return
        }
        if (!this.context.autoplay) {
            return
        }
        if (this.isMouseEnter) {
            if (this.timer) {
                clearTimeout(this.timer)
                this.timer = null
            }
        } else {
            if (this.length > 1) {
                this.go()
            }
        }
    }
    componentWillUnmount(){
        if (this.timer) {
            clearTimeout(this.timer)
            this.timer = null
        }
        if (this.timer1) {
            window.cancelAnimationFrame(this.timer1)
            this.timer1 = null
        }
    }
    componentDidMount(){
        this.context.setLength(this.props.render().length)
    }
    render(){
        let children = this.props.render()
        return(
            <div className={ cssStyle.container }
                onMouseEnter={ () => { this.context.toggle(true) } }
                onMouseLeave={ () => { this.context.toggle(false) } }
            >
                <div className={ cssStyle.item } ref={ this.slideRef }>
                    {
                        React.Children.map(children, (item, index) => {
                            return (
                                <>
                                    <div>{ React.cloneElement(item) }</div>
                                    {
                                        children.length > 1 && index === children.length - 1
                                        ?
                                        <div>{ React.cloneElement(children[0]) }</div>
                                        :
                                        null
                                    }
                                </>
                            )
                        })
                    }
                </div>
            </div>
        )
    }
}

Item.propTypes = {
    render: PropTypes.func
}

class PrevArrow extends React.PureComponent{
    static contextType = context
    constructor(props){
        super(props)
    }
    prev = () => {
        if (this.context.direction) return
        this.context.setDirection('prev', 1)
    }
    render(){
        return(
            <div 
                className={ cssStyle.prev }
                onClick={ this.prev }
                onMouseEnter={ () => { this.context.toggle(true) } }
                onMouseLeave={ () => { this.context.toggle(false) } }
            >
                { this.props.render ? this.props.render() : <div className={ cssStyle.arrow }><span className="icon iconfont">&#xe630;</span></div> }
            </div>
        )
    }
}

PrevArrow.propTypes = {
    render: PropTypes.func
}

class NextArrow extends React.PureComponent{
    static contextType = context
    constructor(props){
        super(props)
    }
    next = () => {
        if (this.context.direction) return
        this.context.setDirection('next', 1)
    }
    render(){
        return(
            <div
                className={ cssStyle.next }
                onClick={ this.next }
                onMouseEnter={ () => { this.context.toggle(true) } }
                onMouseLeave={ () => { this.context.toggle(false) } }
            >
                {  this.props.render ? this.props.render() : <div className={ cssStyle.arrow }><span className="icon iconfont">&#xe630;</span></div> }
            </div>
        )
    }
}

NextArrow.propTypes = {
    render: PropTypes.func
}

class Icon extends React.PureComponent{
    static contextType = context
    constructor(props){
        super(props)
    }
    turn = (index, current) => {
        if (index === current) {
            return
        }
        if (this.context.direction) return
        this.context.setDirection(index > current ? 'next' : 'prev', Math.abs(index - current))
    }
    render(){
        let arr = new Array(this.context.length).fill(null).map((item, index) => {
            return index
        })
        let current = this.context.current === this.context.length ? 0 : this.context.current
        return(
            <div className={ cssStyle.contarol }
                onMouseEnter={ () => { this.context.toggle(true) } }
                onMouseLeave={ () => { this.context.toggle(false) } }
            >
                {
                    this.props.render
                    ?
                    arr.map(index => {
                        let el = this.props.render(index)
                        return React.cloneElement(el, {
                            key: index,
                            onClick: () => {
                                if (el.props.onClick) {
                                    el.props.onClick()
                                }
                                this.turn(index, current)
                            },
                            className: el.props.className + (current === index ? ' carousel-icon-active' : '')
                        })
                    })
                    :
                    arr.map(index => {
                        return <span key={ index } className={ cssStyle.icon + (current === index ? (' ' + cssStyle.active) : '') }
                            onClick={
                                () => {
                                    this.turn(index, current)
                                }
                            }
                        ></span>
                    })
                }
            </div>
        )
    }
}

Icon.propTypes = {
    render: PropTypes.func
}

export default {
    carousel: Carousel,
    item: Item,
    prevArrow: PrevArrow,
    nextArrow: NextArrow,
    icon: Icon
}